//=============================================================================
///	Memory
//=============================================================================

#include "smem.h"
#include "balloc.h"
#include <assert.h>
#include <stdlib.h>		// for exit

#ifndef	NULL
#define	NULL	((void*)0)
#endif
#ifndef	FALSE
#define	FALSE	(0)
#define	TRUE	(1)
#endif


BAlloc s_balloc;
static void (*s_gccb)(void);

#ifdef _DEBUG
static int raise_gc_count = -1;
static int smalloc_count;
void smem_set_raise_gc(int idx) {
	raise_gc_count = idx;
}
int smem_get_alloc_count(void) {
	return smalloc_count;
}
#endif

void smem_init(void* buf, size_t size, void (*gccb)(void)) {
	balloc_init(&s_balloc, buf, size);
	s_gccb = gccb;
}

void* smalloc(size_t size) {
	void* ptr;
#ifdef _DEBUG
	if (smalloc_count++ != raise_gc_count) {
#else
	if (TRUE) {
#endif
		ptr = balloc(&s_balloc, size);
	} else {
		ptr = NULL;
	}
	if (ptr == NULL) {
		(*s_gccb)();
		ptr = balloc(&s_balloc, size);
		if (ptr == NULL) {
			fprintf(stderr, "out of memory\n");
			exit(1);
		}
	}
	assert((((int)ptr) & 3) == 0);
	return ptr;
}

void sfree__(void* ptr) {
	bfree(&s_balloc, ptr);
}


static void typemark(SExp s, SExpPtr* p) {
	void (*mark)(SExp s) = p->hdr.typeinfo->mark;
	if (mark != NULL) {
		(*mark)(s);
	}
}

static void mark_ofstbl(SExpPtr* p) {
	const int* q = p->hdr.typeinfo->sexp_ofstbl;
	if (q != NULL) {
		for (; *q >= 0; ++q) {
			SExp* ps = (SExp*)(((int)p) + *q);
			SMARK(*ps);
		}
	}
}

void SMARK(SExp s) {
	if (sptrp(s)) {
		SExpPtr* p = s.ptr;
		if (mark(&s_balloc, p)) {
			mark_ofstbl(p);
			typemark(s, p);
		}
	}
}

void smark_ptr(void* ptr) {
	mark(&s_balloc, ptr);
}

void ssweep(void) {
	sweep(&s_balloc);
}



#include <stdio.h>
#include "sexp.h"
#include "sstream.h"
void smem_dump(SExp ostrm) {
	union balloc_header* top = &s_balloc.base;
	union balloc_header* p = top;
	printf("free:\n");
	do {
		printf("%p %d\n", p, p->s.nunits * sizeof(union balloc_header));
		p = p->s.ptr;
	} while (p != top);


	{
		printf("used:\n");
		for (p = s_balloc.used; p != NULL; p = p->s.ptr) {
			SExpPtr* pe = (SExpPtr*)(p + 1);
			const char* type = NULL;
			if (pe->hdr.typeinfo != NULL) {
				switch (pe->hdr.typeinfo->type) {
				default:			type = NULL;	break;
				case tCell:			type = "cell";	break;
				case tSymbol:		type = "symbol";	break;
				case tString:		type = "string";	break;
				case tStream:		type = "stream";	break;
				case tSpecialForm:	type = "sform";	break;
				case tSubr:			type = "subr";	break;
				case tLambda:		type = "lambda";	break;
				case tMacro:		type = "macro";	break;
				case tHash:			type = "hash";	break;
				}
			}
			if (type != NULL) {
				printf("%p: %7d: %-10s", p, p->s.nunits * sizeof(union balloc_header), type);
				write(ptr2s(pe), ostrm, TRUE);
				printf("\n");
			} else {
				printf("%p: %7d\n", p, p->s.nunits * sizeof(union balloc_header));
			}
		}
	}
}

